# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

# Script copyright (C) Wengtong Yu
# Contributors: Wengtong Yu

bl_info = {
    "name" : "CSV Importer",
    "author" : "Wentong Yu",
    "version" : (2, 0, 0),
    "location" : "File > Import-Export",
    "description" : "This is csv file importer which can transfer csv plain data to mesh",
    "blender" : (3, 2, 0),
    "category": "Import-Export",
    "warning" : "Still in development",
    "doc_url" : "https://gitee.com/yu_wen_tong/BlenderPlugins",
}

if "bpy" in locals():
    import importlib
    if "import_csv" in locals():
        importlib.reload(import_csv)

import bpy 
from bpy.props import (
    BoolProperty,
    EnumProperty,
    PointerProperty,
    StringProperty,
    CollectionProperty,
)
from bpy_extras.io_utils import(
    orientation_helper,
    axis_conversion,
)
from bpy.types import (
    PropertyGroup,
)

default_token_name = "Undefined"

general_vertex_attr = [
    ("POSITION", "Position", "The vertex position", 0),
    ("COLOR", "Color", "The vertex color", 1),
    ("NORMAL", "Normal", "The vertex normal", 2),
    ("UV_PACKED", "Packed", "Pack custom data to uv", 3),
    ("TEXCOORD", "UV", "The vertex texcoord", 4),
    ("BONEINDICES", "Bone Indices", "The Bone Indices", 5),
    ("BONEWEIGHTS", "Bone Weights", "The Bone Weights", 6),
    ("ATTRIBUTE", "Attribute", "The vertex attributes", 7)
]

class BaseTokenGroup(PropertyGroup):
    """
    Base token group
    """
    def updateToken(self, context):
        from . import import_csv
        if (import_csv.token_name_description is None): 
            print("The token name description is None!")
            return
        for k in import_csv.token_name_description.keys():
            if (k == self.csv_name):
                import_csv.token_name_description[k] = (
                    import_csv.token_name_description[k][0], 
                    import_csv.token_name_description[k][1],
                    self.token_name, 
                    self.normalize,
                    self.token_mask
                )

    token_name : EnumProperty(
        items = general_vertex_attr,
        default = 0,
        name = default_token_name,
        update = updateToken
    )

    token_mask : BoolProperty(
        name = "Enabled",
        default = True,
        update = updateToken
    )

    normalize : BoolProperty(
        name = "Collect In Color", 
        default = False
    )

    csv_name : StringProperty(name = "csv_name", default = "")

class ConfigTokenGroup(PropertyGroup):
    mirror_x : BoolProperty(
        name = "MirroX",
        default = False,
        description = "Mirro the model along x-axis"
    )
    
    vertical_uv : BoolProperty(
        name = "Vertical UV",
        default = False,
        description = "Vertical uv",
    )

    vertex_order : BoolProperty(
        name = "VertexOrder",
        description = "Change vertex order to counter-clockwise order",
        default = False
    )

    merge_vertex : BoolProperty(
        name = "Merge",
        description = "This option can merge split edges",
        default = True
    )

@orientation_helper(axis_forward='-Z', axis_up='Y')
class AddonProperties(PropertyGroup):

    directory_path : StringProperty(
        name = "Directory",
        default = "/tmp",
        subtype = 'DIR_PATH'
    )

    base_tokens : CollectionProperty(type=BaseTokenGroup)
    config_settings : PointerProperty(type=ConfigTokenGroup)

    def validate(self):
        return self.base_tokens is not None and len(self.base_tokens) > 0

    pass


class import_base:
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'CSV Import'

    @classmethod
    def poll(cls, context):
        return bpy.context.mode == 'OBJECT'

class VIEW3D_PT_csv_import_config(import_base, bpy.types.Panel):
    bl_label = "Config"

    def draw(self, context):
        layout = self.layout
        scn = context.scene
        operator = scn.import_csv_settings
        
        layout.prop(operator.config_settings, 'mirror_x')
        layout.prop(operator.config_settings, 'vertex_order')
        layout.prop(operator.config_settings, 'vertical_uv')
        layout.prop(operator.config_settings, 'merge_vertex')
        layout.prop(operator, "axis_forward")
        layout.prop(operator, "axis_up")
        layout.prop(operator, 'directory_path')
        layout.operator('import_mesh.csv_updater', text='Read Tokens')
        layout.operator('import_mesh.csv', text='Import')
        layout.operator('mesh.pack_colors_to_uv', text='Pack colors to uv')


class ImportCSV(bpy.types.Operator):
    """This is the csv import operator"""
    bl_idname = "import_mesh.csv"
    bl_label = "Import CSV"
    bl_options = {'PRESET', 'UNDO'}

    filename_ext = ".csv"
    
    def execute(self, context):
        from . import import_csv
        import os
        print("Execute")
        settings = context.scene.import_csv_settings
        keywords = {}
        global_matrix = axis_conversion(from_forward = settings.axis_forward, from_up = settings.axis_up).to_4x4()
        keywords["global_matrix"] = global_matrix
        keywords["config_settings"] = settings.config_settings
        
        file_list = sorted(os.listdir(settings.directory_path))
        csv_file_list = [item for item in file_list if item.endswith(self.filename_ext)]
        
        for filepath in csv_file_list:
            fileFullPath = os.path.join(settings.directory_path, filepath)
            import_csv.load_csv_data(context, fileFullPath, **keywords)
        return {'FINISHED'}

    def draw(self, context): pass

class PackColorToUV(bpy.types.Operator):
    """This is the pack tool"""
    bl_idname = "mesh.pack_colors_to_uv"
    bl_label = "Pack"
    bl_options = {'PRESET', 'UNDO'}

    packed_name = "packed_"

    def execute(self, context):
        mesh = context.active_object.data
        colorLayers = mesh.vertex_colors
        colorLayerDict = {}
        nameSize = len(self.packed_name)
        for colLayer in colorLayers:
            if colLayer.name[0:nameSize] == self.packed_name:
                colRG = []
                colBA = []
                for item in colLayer.data:
                    colRG.append([item.color[0], item.color[1]])
                    colBA.append([item.color[2], item.color[3]])
                colorLayerDict[colLayer.name[nameSize:]] = (colRG, colBA)
        
        uvLayers = mesh.uv_layers
        names = list(colorLayerDict.keys())
        created = {}
        for uvLayer in uvLayers:
            for name in names:
                # Cannot handle name is not "*.{xy|zw|rg|ba}"
                created[name] = False
                if len(name) < 3: continue
                identifier = uvLayer.name[:-3]
                if identifier == name:
                    created[name] = True

        for k, v in created.items():
            if not v: 
                uvLayers.new(name = k + '.rg')
                uvLayers.new(name = k + '.ba')

        for name in names:
            colRG = colorLayerDict[name][0]
            colBA = colorLayerDict[name][1]
            for i, item in enumerate(uvLayers[name + '.rg'].data):
                item.uv = colRG[i]
            for i, item in enumerate(uvLayers[name + '.ba'].data):
                item.uv = colBA[i]
        return {'FINISHED'}

    def draw(self, context): pass

class UpdateCSV(bpy.types.Operator):
    """This is the csv update operator"""
    bl_idname = "import_mesh.csv_updater"
    bl_label = "Import CSV"
    bl_options = {'PRESET', 'UNDO'}

    filename_ext = ".csv"

    def execute(self, context):
        from . import import_csv
        import os
        print("Execute")
        settings = context.scene.import_csv_settings
        
        file_list = sorted(os.listdir(settings.directory_path))
        csv_file_list = [item for item in file_list if item.endswith(self.filename_ext)]
        
        # load the first file as token reference file
        if csv_file_list is not None and len(csv_file_list) > 0:
            fileFullPath = os.path.join(settings.directory_path, csv_file_list[0])
            import_csv.load_csv_token(context, fileFullPath)

        return {'FINISHED'}

    def draw(self, context): pass

def drawTokenGroup(layout, token):
    box = layout.box()
    box.prop(token, 'token_name', text = token.csv_name)
    box.prop(token, 'token_mask')
    box.prop(token, 'normalize')

class VIEW3D_PT_csv_import_tokens(import_base, bpy.types.Panel):
    bl_label = "CSV Tokens"
    bl_parentid = "VIEW3D_PT_csv_import_config"

    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        layout.use_property_decorate = False  # No animation.

        layout = self.layout
        scn = context.scene
        operator = scn.import_csv_settings
 
        if len(operator.base_tokens.values()) > 0:
            for item in operator.base_tokens:
                drawTokenGroup(layout, item)
                # box = layout.box()
                # box.prop(item, 'token_name', text = item.csv_name)
                # box.prop(item, 'normalize')
        else: 
            box = layout.box()
            box.label(text = "Current does not have csv file tokens!")


classes = (
    BaseTokenGroup,
    ConfigTokenGroup,
    AddonProperties,
    VIEW3D_PT_csv_import_config,
    VIEW3D_PT_csv_import_tokens,
    ImportCSV,
    UpdateCSV,
    PackColorToUV
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.import_csv_settings = PointerProperty(type=AddonProperties)
    

def unregister():
    del bpy.types.Scene.import_csv_settings
    for cls in classes:
        bpy.utils.unregister_class(cls)

if __name__ == "__main__":
    register()
